package com.szwistar.common.comm.jsonrpc.webHost;

import com.szwistar.common.ERR;
import com.szwistar.common.comm.jsonrpc.JsonRpcEndpointAbstract;
import com.szwistar.common.comm.jsonrpc.JsonRpcMessage;
import com.szwistar.common.comm.jsonrpc.JsonRpcMessageCall;
import com.szwistar.common.comm.jsonrpc.JsonRpcMessageNotify;
import com.szwistar.common.comm.jsonrpc.JsonRpcMessageReturn;
import com.szwistar.common.datastruct.JsonMapper;
import com.szwistar.common.datastruct.Utils;

import java.util.ArrayList;
import java.util.List;

/**
 * 基于内置 WebView 的 JsonRpc 服务基类
 */
public abstract class WebHostJsonRpcServer extends JsonRpcEndpointAbstract {
    /**
     * 接口是否已经准备好
     */
    public abstract boolean isReady();

    /**
     * 调用 WebView 中的 JS 对象的函数
     * @param objName JS 对象名（注意一定要是全局对象！）；如果为 null，则默认为 "window" 对象
     * @param funcName 对象成员函数名；如果 obj == null 则实际上是取 window 对象下的函数，也就是全局函数！
     * @param args 可变长参数
     */
    public abstract void callJs(final String objName, final String funcName, final Object... args);

	/**
	 * 提供给 JS 侧的同步直接 RPC 通道
	 * @param method 方法
	 * @param params 参数 JSON
	 * @return 执行结果
	 */
	public abstract String directJsonRpc(String method, String params) throws Exception;

	// 客户端订阅的主题列表
	protected List<String> clientSubscribedTopics = new ArrayList<String>();

    // WebView 页面中的 JsonRpc@WebHost 实例名（注意一定要是全局变量！）
    protected String jsonRpcMessageListener;

	@Override
	public boolean isConnected() {
		return isReady();
	}

	@Override
	public void close() {
		// do nothing
	}

    /**
     * @param linkObj (保留未用)传 null 即可
     * @param caller WebView 页面中的 JsonRpc@WebHost 实例名（注意一定要是全局变量！）
     */
    @Override
    public void listenOn(Object linkObj, String caller) {
    	clientSubscribedTopics.clear();
        jsonRpcMessageListener = caller;
    }

    /**
     * 向 Web 当前页面发送消息
     * @param target 仅在发送通知/Notify消息时，用于标识消息主题；发送其它消息时未用，传 null 即可
     * @param msg 要发送的消息
     * @param qos 未用，传 0 即可
     */
    @Override
    public void send(String target, JsonRpcMessage msg, int qos) throws Exception {
        // 如果页面未在监听，则不用发送了
    	if(Utils.isEmpty(jsonRpcMessageListener)) { return; }
    	// 如果是发送消息通知，先看页面有没有监听此消息；如果没有，就不用发送了
    	if(msg instanceof JsonRpcMessageNotify) {
    		if(!isClientSubscribed(target)) { return; }
    		LOGCOMM.debug("向 Web 页面发送消息通知 {}", msg);
    	}

        // 把消息转换为 JSON 格式
        String jscode = JsonMapper.toJson(msg);
        // 执行回调 JavaScript 代码
        callJs(jsonRpcMessageListener, "onMessage", jscode);
    }

    /**
     * 直接接收 WebView 页面发来的 JsonRpc 消息
     * @param msgJson 消息(JSON 格式)
     */
	public void receiveJsMessage(String msgJson) throws Exception {
    	JsonRpcMessage msg = JsonMapper.fromJson(msgJson, JsonRpcMessage.class);
        super.onMessageReceived(msg);
    }

	/**
	 * 判断某个客户端是否订阅了某个主题的事件
	 * @param topic 主题
	 * @return 是否已经预订
	 */
	protected boolean isClientSubscribed(String topic) {
		return filterTopic(clientSubscribedTopics, topic);
	}

	@Override
	public JsonRpcMessageReturn handleSubscribe(JsonRpcMessageCall request) {
		if(jsonRpcMessageListener == null) { return JsonRpcMessageReturn.error(request, ERR.STATE, "当前客户端未监听，不可订阅消息！"); }
		Object[] filters;

		// 要添加的主题
		filters = request.getParaToArray("add", new String[0]);
		for(Object item: filters) {
			String filter = (String) item;
			if(!validateFilter(filter)) { return JsonRpcMessageReturn.error(request, ERR.BAD_PARAM, "错误的主题过滤器格式！"); }
			if(!clientSubscribedTopics.contains(filter)) { clientSubscribedTopics.add(filter); }
		}

		// 要删除的主题
		filters = request.getParaToArray("del", new String[0]);
		for(Object item: filters) {
			String filter = (String) item;
			clientSubscribedTopics.remove(filter);
		}

		return JsonRpcMessageReturn.ok(request, null);
	}
}
